//
//  Light.hpp
//  RayTracer
//
//  Created by 俞云康 on 2/15/16.
//  Copyright © 2016 yushroom. All rights reserved.
//

#ifndef Light_hpp
#define Light_hpp

#include "Math.hpp"
#include "Color.hpp"
#include "Ray.hpp"
#include "HitInfo.hpp"
#include "Sample.hpp"

class Scene;
class Shape;
class Params;
class GeometricPrimitive;

struct VisibilityTester {
    Ray ray;
    
    void set_segment(const Position& p1, const float3& p2) {
        ray.dir = p2 - p1;
        ray.maxt = ray.dir.normalize_self() * (1 - 10*EPS2);
        ray.o = p1 + EPS*ray.dir;
        Assert(ray.valid());
    }
    void set_ray(const Position &p, const Direction& w) {
        ray.o = p;
        ray.dir = Normalize(w);
        ray.maxt = INFINITY;
        Assert(ray.valid());
    }
    bool unocclude(const Scene& scene) const;
};

//struct LightSample {
//    float uPos[2], uComponent;
//    
//    LightSample() = default;
//    //LightSample(const Sample)
//    LightSample(RNG& rng) {
//        uPos[0] = rng.RandomFloat();
//        uPos[1] = rng.RandomFloat();
//        uComponent = rng.RandomFloat();
//    }
//    LightSample(float up0, float up1, float ucomp) {
//        Assert(up0 >= 0 && up0 < 1);
//        Assert(up1 >= 0 && up1 < 1);
//        Assert(ucomp >= 0 && ucomp < 1);
//        uPos[0] = up0, uPos[1] = up1;
//        uComponent = ucomp;
//    }
//};

class Light {
protected:
    bool cast_shadow;
    
public:
    virtual ~Light() {}
    virtual Color sample_L(const HitInfo& hit, VisibilityTester* visibility) const = 0;
	virtual Color Le(const Ray &r) const {
		return Color(0.f, 0.f, 0.f);
	}
};


//class PointLight : public Light {
//private:
//    float3 position;
//    Color color;
//    float ls;
//    
//public:
//    PointLight(const float3& position, const Color& color, const float ls = 1.0f)
//    : position(position), color(color), ls(ls) {}
//
//    virtual Color sample_L(const HitInfo& hit, VisibilityTester* visibility) const override {
//        visibility->set_segment(hit.position, position);
//        return color * ls / DistanceSquared(position, hit.position);
//    }
//};

class AreaLight : public Light {
public:
    std::shared_ptr<GeometricPrimitive> geometricPrimitive;
    Color intensity;
    //float shape_area;
    
public:
	AreaLight(const Color& intensity);

    AreaLight(std::shared_ptr<GeometricPrimitive> geometricPrimitive, const Color& intensity);
    
	shared_ptr<AreaLight> clone() const {
		return std::make_shared<AreaLight>(this->geometricPrimitive , this->intensity);
	}

    static shared_ptr<AreaLight> create(const Params& params);
    
    virtual Color sample_L(const HitInfo& hit, VisibilityTester* visibility) const override {
        return {0, 0, 0};
    }
    
    Color L(const float3& p, const float3& normal, const float3& w) const {
        return Dot(normal, w) > 0.f ? intensity : Color(0, 0, 0);
    }
    
    Color sample_L(const Position& p, const Sample& sample, Direction* wi, float* pdf, VisibilityTester* visibility) const;
    Color sample_L(const Scene& scene, const Sample& sample, Ray* ray, Normal* Ns, float* pdf) const;
    float pdf(const Position& p, const Direction& wi) const;
};

#endif /* Light_hpp */
